package single;

 class Singleton {
     //volatile禁止指令重排
    private static volatile Singleton instance = null;

    private Singleton() {
        System.out.println(Thread.currentThread().getName() + "------SingltTon构造方法执行了----");
    }
    //DCl实现单例（Double check Lock）双端检锁机制 需要禁止指令重排
     /*
     （*****）DCL(双端检锁)机制不一定线程安全，原因是有指令重排序的存在。
     加入volatile可以禁止指令重排序。
        instance = new Singleton(); 可以分为以下三步完成（伪代码）
        memory = allocate();//1、分配对象内存空间
        instance(memory);//2、初始化对象
        instance = memory;//3设置instance指向刚分配的内存地址，此时instance != null；
     步骤2和步骤3不存在数据的依赖关系，而且无论重排前还是重排后的执行结果在单线程都没有改变。
     因此这种重排序优化是允许的。
     memory = allocate();//1、分配对象内存空间
     instance = memory;//3设置instance指向刚分配的内存地址，此时instance != null；但是对象还没有初始化完成。
      memory = allocate();//1、分配对象内存空间
    指令重排只会保证串行语义的一致性（单线程），但并不会考虑多线程之间的语义的一致性。
    所以当一条线程访问instance不为null时，由于instance实例可能还没有初始化完成，造成线程安全问题。

     原因：某一个线程执行到第一次检测时候，读取到instance不为null，
     instance的引用对象可能没有完成初始化。

      */
    /*public static Singleton getInstance() {
        //加锁前判断是为了提高程序的效率
        if(instance == null) {

            synchronized (Singleton.class) {
            //保证线程安全，保证始终创建一个实例
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }*/
    //synchronized是重量级锁，效率低，代价高。应该多考虑轻量级锁。
   /* public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }*/
   //非线程安全的懒汉式实现单例,多线程环境下会出现线程安全问题
     public static Singleton getInstance() {
         if (instance == null) {
             instance = new Singleton();
         }
         return instance;
     }
}

class Test {
    public static void main(String[] args) {
        for(int i = 1; i < 20; i++) {
            new Thread(() -> {
            //线程执行方法语句
                Singleton.getInstance();
            },String.valueOf(i)).start();
          }
    }
}